package com.googlecode.genproject.service.impl;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.text.DecimalFormat;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.googlecode.genproject.common.Cache;
import com.googlecode.genproject.model.ServiceConfigXml;
import com.googlecode.genproject.service.GenProjectService;
import com.googlecode.genproject.service.MainService;
import com.googlecode.genproject.service.base.ServiceSupport;

/**
 * 主类实现类
 * @author devilishking
 *
 */
public class MainServiceImpl extends ServiceSupport<MainServiceImpl> 
							 implements MainService {
	/**
	* 系统属性文件名
	*/
	private String PROPERTIES_NAME = "systemInfo.properties";

	/**
	 * 执行
	 */
	public void doExecute() {
		try{	
			Properties properties = new Properties();
			properties.load(ClassLoader.getSystemResourceAsStream(PROPERTIES_NAME));
			boolean isPass = false;
			try{
				isPass = checkVersion(properties);
			}
			catch(UnknownHostException e){
				System.out.println();
				System.out.println("网络连接失败!");
				System.out.println();
				isPass = true;
			}
			catch(FileNotFoundException e){
				System.out.println();
				System.out.println("连接不存在!\r\n" + e.getMessage());
				System.out.println();
				isPass = true;
			}
			if (isPass){
				showWelcomeText(properties);
				GenProjectService genProjectService = Cache.getInstance().getBean(ServiceConfigXml.ID_GENPROJECT, GenProjectService.class);
				genProjectService.genProject(properties);
			}
			
			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
			System.out.println();
			System.out.print("请输入任意键以推出系统...");
			in.readLine().trim();
		}
		catch(Exception e){
			setError(e.getMessage(), e);
			System.out.println(e.getMessage());

			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
			System.out.println();
			System.out.print("请输入任意键以推出系统...");
			try{
				in.readLine().trim();
			}
			catch(Exception e2){ }
		}
	}
	
	/**
	 * 校验系统和项目母板版本
	 * @param properties 系统属性
	 * @throws Exception
	 */
	protected Boolean checkVersion(Properties properties) throws Exception{
		System.out.println();
		System.out.println("正在检查更新，请稍候!");

		if(checkSystemVersion(properties)){
			checkRawProjectVersion(properties);
			return true;
		}
		else{ 
			return false;
		}
	}

	/**
	 * 显示欢迎信息
	 * @param properties 系统属性
	 * @throws Exception
	 */
	protected void showWelcomeText(Properties properties) throws Exception{
		String systemVersion = properties.getProperty("system.version");
		String systemLastUpdated = properties.getProperty("system.lastUpdated");
		String organizationName = properties.getProperty("organization.name");
		String organizationUrl = properties.getProperty("organization.url");
		String rawProjectLastVersion = properties.getProperty("rawproject.lastversion");
		
		System.out.println();
		System.out.println("欢迎使用本系统!");
		System.out.println(String.format("版本号: %1$s-%2$s  项目母板版本号: %3$s  系统版权: %4$s  \r\n详情请访问: %5$s", 
						  				 systemVersion, systemLastUpdated, rawProjectLastVersion, organizationName, organizationUrl));
		System.out.println();
	}

	/**
	 * 校验项目母板版本
	 * @param properties 系统属性
	 * @throws Exception
	 */
	private void checkRawProjectVersion(Properties properties) throws Exception{
		String rawprojectUrl = properties.getProperty("rawproject.url");
		String rawProjectLastVersion = properties.getProperty("rawproject.lastversion");
   		String basePath = System.getProperty("user.dir");
	   	
	   	File rawProjectFile = new File(new StringBuilder().append(basePath)
	   											   		  .append("/raw-project-")
	   											   		  .append(rawProjectLastVersion)
	   											   		  .append(".zip").toString());
	   	File oldRawProjectFile = null;
	   	File basePathFolder = new File(basePath);
	   	File[] files = basePathFolder.listFiles();
	   	for(File file : files){
	   		if (file.getName().indexOf("raw-project-") == 0){
	   			oldRawProjectFile = file;
	   		}
	   	}
	   	
	   	boolean hasRawProjectFile = rawProjectFile.exists();
	   	if (!hasRawProjectFile){
	   		System.out.println();
	   		if (oldRawProjectFile != null && oldRawProjectFile.exists()){
	   			if (!inputBoolean("检测到项目母板有最新版本，是否要下载？ true: ", true)) return;
	   			System.out.println("正在下载，请稍候...");
	   		}
	   		else{
	   			System.out.println("正在下载最新项目母板，请稍候...");
	   		}
	   		try{
		   		URL rawProjectUrl = new URL(new StringBuilder().append(rawprojectUrl)
															   .append("raw-project-")
															   .append(rawProjectLastVersion)
															   .append(".zip").toString());
		   		HttpURLConnection httpURLConnection = (HttpURLConnection)rawProjectUrl.openConnection();
				
				DataOutputStream dos = null;
			    BufferedInputStream bis = null;
			    FileOutputStream fos = null;
				try{
		    		fos = new FileOutputStream(new StringBuilder().append(basePath).append("/raw-project-")
		    													  .append(rawProjectLastVersion).append(".zip")
		    													  .toString());
		    	    dos = new DataOutputStream(fos);
		    	    bis = new BufferedInputStream(httpURLConnection.getInputStream());  
		    	    
		    	    Double totalSize = Double.parseDouble(httpURLConnection.getHeaderField("Content-Length"));
		    	    Double downLoadCount = 0.0;
		    	    DecimalFormat format = new DecimalFormat("##0.00"); 
		    	    byte[] buffer = new byte[1024];
		    	    int count = 0;
		    		System.out.println();
		    	    while ((count = bis.read(buffer)) > 0) {
		    	    	downLoadCount += count;
			    		System.out.print(new StringBuilder().append("下载进度: ")
			    											.append(format.format((downLoadCount / totalSize * 100)))
			    											.append("%\r").toString());
		    	    	dos.write(buffer, 0, count);
		    	    } 
	    	        System.out.print("                    \r");
		    	    if (oldRawProjectFile != null && oldRawProjectFile.exists()) oldRawProjectFile.delete();
	    	        System.out.println("下载最新项目母板完成.");
				}
				catch(Exception e){
					throw e;
				}
				finally{
					if (fos != null) fos.close();
					if (bis != null) bis.close();
					if (dos != null) dos.close();
				}
	   		}
	   		catch(Exception e){
	   			if (rawProjectFile.exists()) rawProjectFile.delete();
	   			throw e;
	   		}
	   	}
	}
	
	/**
	 * 校验系统版本
	 * @param properties 系统属性
	 * @return
	 * @throws Exception
	 */
	private boolean checkSystemVersion(Properties properties) throws Exception{	
		String systemName = properties.getProperty("system.name");
		String systemVersion = properties.getProperty("system.version");
		String systemLastUpdated = properties.getProperty("system.lastUpdated");
		String urlStr = new StringBuilder().append(properties.getProperty("system.url"))
										   .append(properties.getProperty("system.groupId").replace(".", "/"))
										   .append("/").append(systemName).append("/").toString();
		URL metadataUrl = new URL(urlStr + "maven-metadata.xml");
		HttpURLConnection httpURLConnection = (HttpURLConnection)metadataUrl.openConnection();
	   	DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
	   	DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
	   	Document doc = documentBuilder.parse(httpURLConnection.getInputStream());
	   	Element root = doc.getDocumentElement();
	    	
	   	NodeList versionsNodeList = root.getElementsByTagName("versions");
	   	Node versionNode = versionsNodeList.item(0);
	   	NodeList versionNodeList = versionNode.getChildNodes();
	   	String lastVersion = systemVersion;
	   	for(int i = 0; i < versionNodeList.getLength(); i++){
	   		Node node = versionNodeList.item(i);
	   		if ("version".equals(node.getNodeName())){
	   			lastVersion = node.getTextContent();
	   		}
	   	}
	    	
	   	NodeList lastUpdatedNodeList = root.getElementsByTagName("lastUpdated");
	   	Node lastUpdatedNode = lastUpdatedNodeList.item(0);
	   	String lastUpdated = lastUpdatedNode.getTextContent();
	   	
	   	if (!lastVersion.equals(systemVersion) || !lastUpdated.equals(systemLastUpdated)){
	   		if (!inputBoolean("检测到系统有最新版本，是否要下载？ true: ", true)) return true;
	   		System.out.println("正在下载，请稍候...");
	   		
	   		String basePath = System.getProperty("user.dir");
	   		metadataUrl = new URL(new StringBuilder().append(urlStr).append(lastVersion).append("/")
	   												 .append("maven-metadata.xml").toString());
			httpURLConnection = (HttpURLConnection)metadataUrl.openConnection();
			doc = documentBuilder.parse(httpURLConnection.getInputStream());
	    	root = doc.getDocumentElement();
	    	NodeList snapshotVersionNodeList = root.getElementsByTagName("snapshot");
	    	Node snapshotVersionNode = snapshotVersionNodeList.item(0);
	    	NodeList dataNodeList = snapshotVersionNode.getChildNodes();
	    	StringBuilder jarName = new StringBuilder().append(systemName).append("-")
	    											   .append(lastVersion.replace("SNAPSHOT", ""));
	    	for(int i = 0; i < dataNodeList.getLength(); i++){
	    		Node node = dataNodeList.item(i);
	    		if ("timestamp".equals(node.getNodeName())){
	    			jarName.append(node.getTextContent()).append("-");
	    		}
	    		if ("buildNumber".equals(node.getNodeName())){
	    			jarName.append(node.getTextContent());
	    		}
	    	}	    		
	   		
			URL jarUrl = new URL(new StringBuilder().append(urlStr)
													.append(lastVersion).append("/")
													.append(jarName).append(".jar")
													.toString());
			httpURLConnection = (HttpURLConnection)jarUrl.openConnection();
			String jarPath = new StringBuilder().append(basePath)
												.append("/").append(systemName)
												.append(".jar").toString();
			String jarBakPath = new StringBuilder().append(basePath)
												   .append("/").append(systemName)
												   .append(".jar.bak").toString();
				
			readWriteJar(jarPath, jarBakPath, null);
				
			DataOutputStream dos = null;
		    BufferedInputStream bis = null;
		    FileOutputStream fos = null;
			try{
	    		fos = new FileOutputStream(jarPath);
	    	    dos = new DataOutputStream(fos);
	    	    bis = new BufferedInputStream(httpURLConnection.getInputStream());  
	    	    
	    	    Double totalSize = Double.parseDouble(httpURLConnection.getHeaderField("Content-Length"));
	    	    Double downLoadCount = 0.0;
	    	    DecimalFormat format = new DecimalFormat("##0.00"); 
	    	    byte[] buffer = new byte[1024];
	    	    int count = 0;
	    		System.out.println();
	    	    while ((count = bis.read(buffer)) > 0) {
	    	    	downLoadCount += count;
		    		System.out.print(new StringBuilder().append("下载进度: ")
		    											.append(format.format((downLoadCount / totalSize * 100)))
		    											.append("%\r").toString());
	    	    	dos.write(buffer, 0, count);
	    	    } 
    	        System.out.print("                    \r");
			}
			catch(Exception e){
				throw e;
			}
			finally{
				if (fos != null) fos.close();
				if (bis != null) bis.close();
				if (dos != null) dos.close();
			}	
			
			String jarSha1 = getHash(jarPath);
			String newJarSha1 = "";
			URL jarSha1Url = new URL(new StringBuilder().append(urlStr)
														.append(lastVersion).append("/")
														.append(jarName).append(".jar.sha1")
														.toString());
			httpURLConnection = (HttpURLConnection)jarSha1Url.openConnection();
			BufferedReader in = null;
			try{
				in = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
				newJarSha1 = in.readLine();
			}
			catch(Exception e){
				throw e;
			}
			finally{
				if (in != null) in.close();
			}
			if (!newJarSha1.equals(jarSha1)){
				System.out.println();
		    	System.out.println("jar包文件下载内容与服务器的内容不一致，请重新下载!");
		   		readWriteJar(jarBakPath, jarPath, null);
				new File(jarBakPath).delete();
		    	return false;
			}
			
	    	properties.setProperty("system.version", lastVersion);
	    	properties.setProperty("system.lastUpdated", lastUpdated);
			fos = null;
			String propertiesPath = new StringBuilder().append(basePath)
			   										   .append("/").append(PROPERTIES_NAME)
			   										   .toString();
			try{
				fos = new FileOutputStream(propertiesPath);
				properties.store(fos, new StringBuilder().append("update systemVersion: ").append(lastVersion)
														 .append(" lastUpdated: ").append(lastUpdated)
														 .toString());
			}
			catch(Exception e){
				throw e;
			}
			finally{
				if (fos != null) fos.close();
			}
				
			readWriteJar(jarPath, jarPath, propertiesPath);
			
			new File(jarBakPath).delete();
				
	    	System.out.println();
	    	System.out.println("系统更新完毕，请重新启动系统!");
	    	return false;
	    }
	    else{
	    	System.out.println();
	    	System.out.println("已是最新版本!");
	    	return true;
	    }
	}

	/**
	 * 处理jar包
	 * @param srcPath		 jar包源路径
	 * @param targetPath     jar包目标路径
	 * @param propertiesPath 系统属性文件路径
	 * @throws Exception
	 */
	private void readWriteJar(String srcPath, String targetPath, String propertiesPath) throws Exception{
		BufferedInputStream bIn = null; 
        JarFile jar = null;
		Hashtable<String,byte[]> table = new Hashtable <String,byte[]>(); 
		try{
			jar = new JarFile(srcPath);
	        Enumeration<JarEntry> entries = jar.entries(); 
	        while(entries.hasMoreElements()){ 
	        	JarEntry entry = entries.nextElement();
	        	if(entry.getName().indexOf(".") > -1){
	        		if(!PROPERTIES_NAME.equals(entry.getName()) || propertiesPath == null){
	        			bIn = new BufferedInputStream(jar.getInputStream(entry));
	        			int len = bIn.available();
	        			byte[] bt = new byte[len];
	        			bIn.read(bt);
	        			bIn.close();
	        			table.put(entry.getName(), bt);
	        		}
	        	}
	        }
		}
		catch(Exception e){
			throw e;
		}
		finally{
			if (jar != null) jar.close();
			if (bIn != null) bIn.close();
		}
		
		JarOutputStream jOut = null;
		FileInputStream inf = null;
		File propertiesFile = null; 
		JarEntry entry = null;
		try{
			jOut = new JarOutputStream(new FileOutputStream(targetPath)); 
			if (propertiesPath != null){
				propertiesFile = new File(propertiesPath);
				entry = new JarEntry(PROPERTIES_NAME); 
				jOut.putNextEntry(entry); 
				
				inf = new FileInputStream(propertiesFile);  
				byte[] bt = new byte[1024];  
	            int count;  
	            while ((count = inf.read(bt)) > 0) {  
	            	jOut.write(bt, 0, count);  
	            }  
				
				jOut.flush();
			}
			
			Enumeration <String> names = table.keys(); 
            while(names.hasMoreElements()){ 
                String entryName = names.nextElement(); 
                entry = new JarEntry(entryName); 
                jOut.putNextEntry(entry);                
                jOut.write(table.get(entryName)); 
                jOut.flush(); 
            } 
		}
		catch(Exception e){
			throw e;
		}
		finally{
			if (jOut != null) jOut.close();		
			if (inf != null) inf.close();					
		}
		if (propertiesPath != null) propertiesFile.delete();
	}
	
	/**
	 * 获取文件sha1码
	 * @param fileName
	 * @return
	 * @throws Exception
	 */
	private String getHash(String fileName) throws Exception {  
		InputStream fis = null;
		try{
			fis = new FileInputStream(fileName);  
			byte[] buffer = new byte[1024];  
			MessageDigest md5 = MessageDigest.getInstance("SHA1");  
			int numRead = 0;  
			while ((numRead = fis.read(buffer)) > 0) {  
			    md5.update(buffer, 0, numRead);  
			}  
			return toHexString(md5.digest());
		}
		catch(Exception e){
			throw e;
		}
		finally{
			if (fis != null) fis.close();
		}
	}  
	
	/**
	 * 生成sha1码
	 * @param b
	 * @return
	 */
	private String toHexString(byte[] b) {  
	    char[] hexChar = {'0', '1', '2', '3',  
					      '4', '5', '6', '7',  
					      '8', '9', 'a', 'b',  
					      'c', 'd', 'e', 'f'};  
		StringBuilder sb = new StringBuilder(b.length * 2);  
		for (int i = 0; i < b.length; i++) {  
		    sb.append(hexChar[(b[i] & 0xf0) >>> 4]);  
		    sb.append(hexChar[b[i] & 0x0f]);  
		}  
		return sb.toString();  
	}
}